
package com.probiz.estore.system.service.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import com.probiz.estore.Constants;
import com.probiz.estore.aop.VerFuncInterceptor;
import com.probiz.estore.catalog.service.ProductCategoryManager;
import com.probiz.estore.catalog.service.VirtualCategoryManager;
import com.probiz.estore.common.helper.ConfigUtil;
import com.probiz.estore.common.model.catalog.ProductCategory;
import com.probiz.estore.common.model.catalog.VirtualCategory;
import com.probiz.estore.common.model.product.Brand;
import com.probiz.estore.common.model.product.Product;
import com.probiz.estore.common.util.DateUtil;
import com.probiz.estore.core.search.SearchCriteria;
import com.probiz.estore.product.service.BrandManager;
import com.probiz.estore.product.service.ProductManager;
import com.probiz.estore.system.service.SiteMapManager;

public class SiteMapManagerImpl implements SiteMapManager {
	private static final transient Log	logger		= LogFactory
															.getLog(SiteMapManagerImpl.class);
	
	private static final String SITEMAP_DIR = "sitemap/";
	private static final String SITEMAP_INDEX = "sitemap.xml";

	private ProductManager	productManager	= null;
	private ProductCategoryManager productCategoryManager = null;
	private VirtualCategoryManager virtualCategoryManager = null;
	private BrandManager brandManager = null;

	public void setProductManager(ProductManager productManager) {
		this.productManager = productManager;
	}
	
	public ProductManager getProductManager() {
		return productManager;
	}
	

	public void setProductCategoryManager(
			ProductCategoryManager productCategoryManager) {
		this.productCategoryManager = productCategoryManager;
	}

	public void setVirtualCategoryManager(
			VirtualCategoryManager virtualCategoryManager) {
		this.virtualCategoryManager = virtualCategoryManager;
	}

	public void setBrandManager(BrandManager brandManager) {
		this.brandManager = brandManager;
	}

	public void generateSiteMap() {
		//生成关键页面的sitemap
		List<String> keyPageFilename = generateKeyPageSiteMap();
		//生成productCategory的sitemap
		List<String> productCategorySitemapFilenames = generateProductCategorySiteMap();
		//生成virtualCategory的sitemap
		List<String> virtualCategorySitemapFilenames=new ArrayList<String>();
		try {
			if(VerFuncInterceptor.getInstance().invoke(VerFuncInterceptor.Constants.virtualCategory)){
				virtualCategorySitemapFilenames = generateVirtualCategorySiteMap();
			}
		} catch (Exception e) {
			logger.error(this, e);
		}
		//生成brand的sitemap
		List<String> brandSitemapFilenames = generateBrandSiteMap();
		//生成product的sitemap
		List<String> productSitemapFilenames = generateProductSiteMap();
		//...在此加入其它模块的sitemap
		
		//生成索引
		generateIndex(keyPageFilename,productCategorySitemapFilenames,virtualCategorySitemapFilenames,brandSitemapFilenames,productSitemapFilenames);
		
	}
	
	/**
	 * 生成索引 sitemap
	 */
	private void generateIndex(List<String>... filenameList){
		Document document = DocumentHelper.createDocument();
		document.addProcessingInstruction("xml-stylesheet ", "type=\"text/xsl\" href=\"gss.xsl\"");
		Element sitemapindexElement =document.addElement("sitemapindex","http://www.sitemaps.org/schemas/sitemap/0.9");
		sitemapindexElement.addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
		sitemapindexElement.addAttribute("xsi:schemaLocation", "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd");
		String storeFrontSiteMapUrl = getStoreFrontSiteMapUrl();
		Date date = new Date();
		for(List<String> filenames : filenameList){
			for(String filename: filenames){
				Element sitemapElement = sitemapindexElement.addElement("sitemap");
				(sitemapElement.addElement("loc")).addText(storeFrontSiteMapUrl + filename);
				(sitemapElement.addElement("lastmod")).addText(DateUtil.convertDateToString(date, ConfigUtil.getInstance().getDatePattern()));
			}
		}
		
		//写入XML文件
		try{
			writeNotmalFile(getIndexSiteMapInstallationPath()+SITEMAP_INDEX, document);
		}catch(Exception e)	{
			e.printStackTrace();
		}
		
	}

	/**
	 * 生成商品的sitemap
	 * @return 所有这些sitemap文件的文件名
	 */
	private List<String> generateKeyPageSiteMap() {
		//获得XML document
		Document document = buildKeyPageSiteMapXMLDocument();				
		String fileName =  "summary.xml";
		//写入XML文件
		try{
			writeNotmalFile(getStoreFrontSiteMapInstallationPath()+fileName, document);
		}catch(Exception e)	{
			e.printStackTrace();
		}
		List list = new ArrayList<String>();
		list.add(fileName);
		return list;
	}

	/**
	 * 创建一份包含关键页面的Document
	 * @return
	 */
	private Document buildKeyPageSiteMapXMLDocument(){
		Document document = DocumentHelper.createDocument();
		Element urlSetElement = buildSitemapStyleSheetAndNamespace(document);
		buildKeyPageElement(urlSetElement);
		return document;
	}
	/**
	 * 组装关键页面Element(dom4j)对象
	 * @param parent
	 */
	private void buildKeyPageElement(Element parent){
		List<String> pageUrlList = new ArrayList<String>();
		try{
			if(VerFuncInterceptor.getInstance().invoke(VerFuncInterceptor.Constants.promoRule)){
				pageUrlList.add("/promo.html");
				pageUrlList.add("/giftCouponQuery.html");
			}
			if(VerFuncInterceptor.getInstance().invoke(VerFuncInterceptor.Constants.giftCertificate)){
				pageUrlList.add("/buyGiftCertificate.html");
			}
		}catch(Exception e){
			logger.error(this, e);
		}
		pageUrlList.add("/sitemap.html");
		
		Date now = new Date();
		for(String pageUrl: pageUrlList){
			Element element = parent.addElement("url");
			(element.addElement("loc")).addText(getStoreFrontSiteUrl()+pageUrl);
			(element.addElement("lastmod")).addText(DateUtil.convertDateToString(now, ConfigUtil.getInstance().getDatePattern()));
			(element.addElement("changefreq")).addText("daily");
			(element.addElement("priority")).addText("1");
		}
	}
	
	
	
	/**
	 * 生成商品的sitemap
	 * @return 所有这些sitemap文件的文件名
	 */
	private List<String> generateProductSiteMap() {
		List<String> filenames = new ArrayList<String>();
		String hql = "from Product p where p.status = 1";//获得上架商品
		boolean flag =true;
		int pageSize = 1000;
		int pageNo = 1;
		do{
			SearchCriteria sc = SearchCriteria.getHqlPagingInstance(hql,null,pageNo,pageSize,null);
			List<Product> list = productManager.searchByCriteria(sc);
			//获得XML document
			Document document = buildProductSiteMapXMLDocument(list);				
			//写入XML文件
			try{
				String fileName =  "product_" + pageNo + ".xml";
				writeNotmalFile(getStoreFrontSiteMapInstallationPath()+fileName, document);
				filenames.add(fileName);
			}catch(Exception e)	{
				e.printStackTrace();
			}
			
			if(list.size()!=pageSize){
				flag=false;
			}else{
				++pageNo;
			}
		}while(flag);	 
		return filenames;
	}

	/**
	 * 将商品数据组装成Element(dom4j)对象
	 * @param product
	 * @return
	 */
	private void buildProductElement(Element parent,Product product) {
		Element element = parent.addElement("url");
		(element.addElement("loc")).addText(getStoreFrontSiteUrl()+productManager.getUriByProduct(product));
		(element.addElement("lastmod")).addText(DateUtil.convertDateToString(product.getUpdateTime(), ConfigUtil.getInstance().getDatePattern()));
		(element.addElement("changefreq")).addText("daily");
		(element.addElement("priority")).addText("0.5");
		return;
	}
	/**
	 * 创建一份包含商品数据的Document
	 * @return
	 */
	private Document buildProductSiteMapXMLDocument(List<Product> productList){
		Document document = DocumentHelper.createDocument();
		Element urlSetElement = buildSitemapStyleSheetAndNamespace(document);
		
		for(Product product:productList){
			buildProductElement(urlSetElement,product);
		}
		return document;
	}
	
	
	/**
	 * 生成商品目录的sitemap
	 * @return 所有这些sitemap文件的文件名
	 */
	private List<String> generateProductCategorySiteMap() {
		List<String> filenames = new ArrayList<String>();
		String hql = "from ProductCategory pc where pc.status = 1 and pc.categoryId != "+Constants.ROOT_CATEGORY;//获得启用的商品目录
		boolean flag =true;
		int pageSize = 1000;
		int pageNo = 1;
		do{
			SearchCriteria sc = SearchCriteria.getHqlPagingInstance(hql,null,pageNo,pageSize,null);
			List<ProductCategory> list = productCategoryManager.searchByCriteria(sc);
			//获得XML document
			Document document = buildProductCategorySiteMapXMLDocument(list);				
			//写入XML文件
			try{
				String fileName =  "productCategory_" + pageNo + ".xml";
				writeNotmalFile(getStoreFrontSiteMapInstallationPath()+fileName, document);
				filenames.add(fileName);
			}catch(Exception e)	{
				e.printStackTrace();
			}
			
			if(list.size()!=pageSize){
				flag=false;
			}else{
				++pageNo;
			}
		}while(flag);	 
		return filenames;
	}

	/**
	 * 将商品目录数据组装成Element(dom4j)对象
	 * @param product
	 * @return
	 */
	private void buildProductCategoryElement(Element parent,ProductCategory productCategory) {
		Element element = parent.addElement("url");
		(element.addElement("loc")).addText(getStoreFrontSiteUrl()+productCategoryManager.getUriByCategoryId(productCategory.getCategoryId()));
		(element.addElement("lastmod")).addText(DateUtil.convertDateToString(productCategory.getUpdateTime(), ConfigUtil.getInstance().getDatePattern()));
		(element.addElement("changefreq")).addText("daily");
		(element.addElement("priority")).addText("0.8");
		return;
	}
	/**
	 * 创建一份包含商品目录数据的Document
	 * @return
	 */
	private Document buildProductCategorySiteMapXMLDocument(List<ProductCategory> productCategoryList){
		Document document = DocumentHelper.createDocument();
		Element urlSetElement = buildSitemapStyleSheetAndNamespace(document);
		for(ProductCategory productCategory:productCategoryList){
			buildProductCategoryElement(urlSetElement,productCategory);
		}
		return document;
	}
	
	
	/**
	 * 生成专场目录的sitemap
	 * @return 所有这些sitemap文件的文件名
	 */
	private List<String> generateVirtualCategorySiteMap() {
		List<String> filenames = new ArrayList<String>();
		String hql = "from VirtualCategory vc where vc.status = 1 and vc.categoryId != "+Constants.ROOT_CATEGORY;//获得启用的商品目录
		boolean flag =true;
		int pageSize = 1000;
		int pageNo = 1;
		do{
			SearchCriteria sc = SearchCriteria.getHqlPagingInstance(hql,null,pageNo,pageSize,null);
			List<VirtualCategory> list = virtualCategoryManager.searchByCriteria(sc);
			//获得XML document
			Document document = buildVirtualCategorySiteMapXMLDocument(list);				
			//写入XML文件
			try{
				String fileName =  "virtualCategory_" + pageNo + ".xml";
				writeNotmalFile(getStoreFrontSiteMapInstallationPath()+fileName, document);
				filenames.add(fileName);
			}catch(Exception e)	{
				e.printStackTrace();
			}
			
			if(list.size()!=pageSize){
				flag=false;
			}else{
				++pageNo;
			}
		}while(flag);	 
		return filenames;
	}

	/**
	 * 将专场目录数据组装成Element(dom4j)对象
	 * @param product
	 * @return
	 */
	private void buildVirtualCategoryElement(Element parent,VirtualCategory virtualCategory) {
		Element element = parent.addElement("url");
		(element.addElement("loc")).addText(getStoreFrontSiteUrl()+virtualCategoryManager.getUriByCategoryId(virtualCategory.getCategoryId()));
		(element.addElement("lastmod")).addText(DateUtil.convertDateToString(virtualCategory.getUpdateTime(), ConfigUtil.getInstance().getDatePattern()));
		(element.addElement("changefreq")).addText("daily");
		(element.addElement("priority")).addText("0.8");
		return;
	}
	/**
	 * 创建一份包含专场目录数据的Document
	 * @return
	 */
	private Document buildVirtualCategorySiteMapXMLDocument(List<VirtualCategory> virtualCategoryList){
		Document document = DocumentHelper.createDocument();
		Element urlSetElement = buildSitemapStyleSheetAndNamespace(document);
		
		for(VirtualCategory virtualCategory:virtualCategoryList){
			buildVirtualCategoryElement(urlSetElement,virtualCategory);
		}
		return document;
	}
	
	
	/**
	 * 生成品牌的sitemap
	 * @return 所有这些sitemap文件的文件名
	 */
	private List<String> generateBrandSiteMap() {
		List<String> filenames = new ArrayList<String>();
		String hql = "from Brand b";//获得启用的商品目录
		boolean flag =true;
		int pageSize = 1000;
		int pageNo = 1;
		do{
			SearchCriteria sc = SearchCriteria.getHqlPagingInstance(hql,null,pageNo,pageSize,null);
			List<Brand> list = brandManager.searchByCriteria(sc);
			//获得XML document
			Document document = buildBrandSiteMapXMLDocument(list);				
			//写入XML文件
			try{
				String fileName =  "brand_" + pageNo + ".xml";
				writeNotmalFile(getStoreFrontSiteMapInstallationPath()+fileName, document);
				filenames.add(fileName);
			}catch(Exception e)	{
				e.printStackTrace();
			}
			
			if(list.size()!=pageSize){
				flag=false;
			}else{
				++pageNo;
			}
		}while(flag);	 
		return filenames;
	}

	/**
	 * 将品牌数据组装成Element(dom4j)对象
	 * @param product
	 * @return
	 */
	private void buildBrandElement(Element parent,Brand brand) {
		Element element = parent.addElement("url");
		(element.addElement("loc")).addText(getStoreFrontSiteUrl()+brandManager.getUriByBrandId(brand.getBrandId()));
		(element.addElement("lastmod")).addText(DateUtil.convertDateToString(new Date(), ConfigUtil.getInstance().getDatePattern()));
		(element.addElement("changefreq")).addText("daily");
		(element.addElement("priority")).addText("0.7");
		return;
	}
	/**
	 * 创建一份包含品牌数据的Document
	 * @return
	 */
	private Document buildBrandSiteMapXMLDocument(List<Brand> brandList){
		Document document = DocumentHelper.createDocument();
		Element urlSetElement = buildSitemapStyleSheetAndNamespace(document);
		
		for(Brand brand:brandList){
			buildBrandElement(urlSetElement,brand);
		}
		return document;
	}
	
	/**
	 * 创建sitemap的styleSheet和namespace
	 * @param document
	 * @return
	 */
	private Element buildSitemapStyleSheetAndNamespace(Document document) {
		document.addProcessingInstruction("xml-stylesheet ", "type=\"text/xsl\" href=\"../gss.xsl\"");
		Element urlSetElement = document.addElement("urlset","http://www.sitemaps.org/schemas/sitemap/0.9");
		urlSetElement.addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
		urlSetElement.addAttribute("xsi:schemaLocation", "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd");
		return urlSetElement;
	}
	/**
	 * 获得前台地址
	 * @return
	 */
	private String getStoreFrontSiteUrl() {
		//start:获得前台地址
		String storeFrontSiteUrl = ConfigUtil.getInstance().getStoreFrontSiteUrl();
		if(storeFrontSiteUrl.endsWith("/") || storeFrontSiteUrl.endsWith("/")){
			storeFrontSiteUrl = storeFrontSiteUrl.substring(0,storeFrontSiteUrl.length()-1);
		}
		return storeFrontSiteUrl;
	}
	
	/**
	 * 获得前台index sitemap文件存放路径 
	 * @return
	 * @throws Exception
	 */
	private String getIndexSiteMapInstallationPath() throws Exception{
		String installationPath = ConfigUtil.getInstance().getStoreFrontInstallationPath();
		if (installationPath == null || "".equals(installationPath)) {
			logger.warn("请设置前台安装路径！");
			throw new Exception("请设置前台安装路径！");
		}
		if (!installationPath.endsWith("/")) {
			installationPath += "/";
		}
		return installationPath;
		
	}
	/**
	 * 获得前台sitemap文件存放路径
	 */
	private String getStoreFrontSiteMapInstallationPath() throws Exception{
		String installationPath = ConfigUtil.getInstance().getStoreFrontInstallationPath();
		if (installationPath == null || "".equals(installationPath)) {
			logger.warn("请设置前台安装路径！");
			throw new Exception("请设置前台安装路径！");
		}
		if (!installationPath.endsWith("/")) {
			installationPath += "/";
		}
		installationPath += SITEMAP_DIR;
		return installationPath;
		
	}
	/**
	 * 获得前台sitemap访问地址
	 * @return
	 */
	private String getStoreFrontSiteMapUrl(){
		String url = ConfigUtil.getInstance().getStoreFrontSiteUrl();
		url += "/"+SITEMAP_DIR;
		return url;
	}
	
	/**
	 * 将字符串写入普通文件
	 * @param file
	 * @param stringValue
	 */
	private void writeNotmalFile(String file, Document document) {
		try {
			OutputFormat format = OutputFormat.createPrettyPrint();
			/** 指定XML字符集编码 */
			format.setEncoding("UTF-8");
			XMLWriter output = new XMLWriter(new FileWriter(new File(file)),format);
            output.write(document);
            output.close();  
		} catch (IOException e) {
			e.printStackTrace();
		} 
	}
	/**
	 * 将字符串数据压缩成GZIP格式 (暂时不适用gzip压缩)
	 * 
	 * @param file
	 * @param stringValue
	 */
	private void writeGzipFile(String file, Document document) {
		try {
			OutputFormat format = OutputFormat.createPrettyPrint();
			/** 指定XML字符集编码 */
			format.setEncoding("UTF-8");
			XMLWriter output = new XMLWriter(new GZIPOutputStream(new FileOutputStream(file)),format);
            output.write(document);
            output.close();  
		} catch (IOException e) {
			e.printStackTrace();
		} 
		
	}
}